# coding: utf-8
from enum import Enum
from typing import Union

import allure
from playwright.sync_api import sync_playwright, Browser, BrowserContext

from common import BrowserTypes, logger
from models import web_test_case_validate, FuncStepModel, AssertDataModel
from .playwright_util import PlayWrightUtil
from ..public import AssertUtil


def handle_callback(step: FuncStepModel, func, page: PlayWrightUtil):
    """
    处理回调函数
    :param step:
    :param func: 函数对象
    :param page: 页面对象
    :return:
    """
    n = 0
    while step.callback is not None:
        text = f'执行回调函数: {step.name}'
        with allure.step(text):
            logger.info(text)
            func = getattr(func, step.func)(*callback.args, **callback.kwargs)
            callback = step.callback
        allure.attach(page.screenshot(), f"截图 {step.name} - {n}", allure.attachment_type.PNG)
        n += 1


def WebCaseMainFunction(case_data: dict, bowser_context: BrowserContext):
    """
    该函数用于运行 UI 测试用例。
    Web UI test case main function.
    :param case_data: 测试用例数据
    :param bowser_context: 浏览器上下文
    :return:
    """
    case = web_test_case_validate(case_data)
    if case.is_new_window:
        page = bowser_context.new_page()
    else:
        try:
            page = bowser_context.pages[case.page_index]
        except Exception as e:
            page = bowser_context.new_page()
            logger.error(f'获取页面失败, 请检查页面索引是否正确: {case.page_index}, 错误信息: {e}')

    page = PlayWrightUtil(page)
    allure.dynamic.feature(case.module_name)
    allure.dynamic.story(case.ui_name)
    allure.dynamic.title(case.case_name)
    allure.dynamic.description(case.case_description)
    allure.dynamic.tag(case.case_tags)
    text = '*' * 20 + f'用例开始, 请耐心等待, {case.case_name}, 正在执行中...' + '*' * 20
    with allure.step(text):
        logger.info(text)
        for step in case.steps:
            step: FuncStepModel
            text = f'执行步骤: {step.name}'
            with allure.step(text):
                logger.info(text)
                func = getattr(page, step.func)(*step.args, **step.kwargs)
                with allure.step(f'获取页面截图'):
                    logger.info(f'获取页面截图')
                    allure.attach(page.screenshot(), "截图", allure.attachment_type.PNG)
                if step.callback:
                    handle_callback(step, func, page)
    text = '开始执行断言'
    with allure.step(text):
        logger.info(text)
        for assert_data in case.assert_data:
            assert_data: AssertDataModel
            if assert_data.method not in dir(AssertUtil()):
                raise ValueError(
                    f'Invalid assert method: {assert_data.method}\n '
                    'Please check the assert method in AssertUtil.\n'
                    f'Valid assert methods are: {dir(AssertUtil())}'

                )
            text = f'执行断言: {assert_data.dict()}'
            with allure.step(text):
                logger.info(text)
                actual_data: FuncStepModel = FuncStepModel(**assert_data.actual)
                actual = getattr(page, actual_data.func)(*actual_data.args, **actual_data.kwargs)
                getattr(AssertUtil, assert_data.method)(actual, assert_data.expected)
    text = '*' * 20 + '用例结束' + '*' * 20
    with allure.step(text):
        logger.info(text)


def get_browser_context(browser_type: Union[Enum, str] = BrowserTypes.Chromium,
                        headless: bool = False) -> BrowserContext:
    """
    获取浏览器上下文
    :param browser_type: 浏览器类型
    :param headless: 是否启用无头模式
    :return:
    """
    logger.info(f'获取浏览器上下文: {browser_type}, 无头模式: {headless}')
    if isinstance(browser_type, BrowserTypes):
        browser_type = browser_type.value
    if browser_type not in BrowserTypes.values():
        raise ValueError(f'Invalid browser type: {browser_type}\n '
                         'Please check the browser type in BrowserTypes.\n'
                         f'Valid browser types are: {BrowserTypes.values()}')
    with sync_playwright() as playwright:
        browser: Browser = getattr(playwright, browser_type).launch(headless=headless, args=['--start-maximized'])
        browser_context = browser.new_context(no_viewport=True)
        return browser_context
